#-
# ==========================================================================
# Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
# rights reserved.
#
# The coded instructions, statements, computer programs, and/or related 
# material (collectively the "Data") in these files contain unpublished 
# information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
# licensors, which is protected by U.S. and Canadian federal copyright 
# law and by international treaties.
#
# The Data is provided for use exclusively by You. You have the right 
# to use, modify, and incorporate this Data into other products for 
# purposes authorized by the Autodesk software license agreement, 
# without fee.
#
# The copyright notices in the Software and this entire statement, 
# including the above license grant, this restriction and the 
# following disclaimer, must be included in all copies of the 
# Software, in whole or in part, and all derivative works of 
# the Software, unless such copies or derivative works are solely 
# in the form of machine-executable object code generated by a 
# source language processor.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
# AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
# WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
# NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
# PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
# TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
# BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
# DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
# AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
# OR PROBABILITY OF SUCH DAMAGES.
#
# ==========================================================================
#+

#
# Creation Date:   4 October 2006
#
# Example Plugin: simpleSolverNode.py
#
#
# Single-bone, single-plane ik-solver.
#
# This plugin demonstrates how to create and register an ik-solver.
# Due to the complex nature of ik solvers, this plugin only 
# works with 2-joint skeletons (1 bone) in the x-y plane.
#
# To use the solver, create a single bone (joint tool).
# Then type the following in the command window:
#
#	import maya.cmds as cmds
#
#	cmds.createNode("spSimpleSolverNode", name="spSimpleSolverNode1")
#   cmds.ikHandle(sol="spSimpleSolverNode1", sj="joint1", ee="joint2")
#
# This creates a handle that can be dragged around in the x-y
# plane.
#

# imports
import maya.OpenMaya as OpenMaya
import maya.OpenMayaUI as OpenMayaUI
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaAnim as OpenMayaAnim
import math, sys

# consts
kSolverNodeName = "spSimpleSolverNode"
kSolverNodeId = OpenMaya.MTypeId(0x8700a)


class simpleSolverNode(OpenMayaMPx.MPxIkSolverNode):
	def __init__(self):
		OpenMayaMPx.MPxIkSolverNode.__init__(self)


	def solverTypeName(self):
		return kSolverNodeName

	
	def doSolve(self):
		self.doSimpleSolver()


	def doSimpleSolver(self):
		"""
		Solve single joint in the x-y plane
			- first it calculates the angle between the handle and the end-effector.
			- then it determines which way to rotate the joint.
		"""
		handle_group = self.handleGroup()
		handle = handle_group.handle(0)
		handlePath = OpenMaya.MDagPath.getAPathTo(handle)
		fnHandle = OpenMayaAnim.MFnIkHandle(handlePath)

		# Get the position of the end_effector
		end_effector = OpenMaya.MDagPath()
		fnHandle.getEffector(end_effector)
		tran = OpenMaya.MFnTransform(end_effector)
		effector_position = tran.rotatePivot(OpenMaya.MSpace.kWorld)

		# Get the position of the handle
		handle_position = fnHandle.rotatePivot(OpenMaya.MSpace.kWorld)

		# Get the start joint position
		start_joint = OpenMaya.MDagPath()
		fnHandle.getStartJoint(start_joint)
		start_transform = OpenMaya.MFnTransform(start_joint)
		start_position = start_transform.rotatePivot(OpenMaya.MSpace.kWorld)

		# Calculate the rotation angle
		v1 = start_position - effector_position
		v2 = start_position - handle_position
		angle = v1.angle(v2)

		# -------- Figure out which way to rotate --------
		#
		#  define two vectors U and V as follows
		#  U   =   EndEffector(E) - StartJoint(S)
		#  N   =   Normal to U passing through EndEffector
		#
		#  Clip handle_position to half-plane U to determine the region it
		#  lies in. Use the region to determine  the rotation direction.
		#
		#             U
		#             ^              Region      Rotation
		#             |  B           
		#            (E)---N            A          C-C-W
		#         A   |                 B           C-W
		#             |  B
		#             |
		#            (S)
		#

		rot = 0.0	# Rotation about Z-axis

		# U and N define a half-plane to clip the handle against
		U = effector_position - start_position
		U.normalize()

		# Get a normal to U
		zAxis = OpenMaya.MVector(0.0, 0.0, 1.0)
		N = U ^ zAxis # Cross product
		N.normalize()

		# P is the handle position vector
		P = handle_position - effector_position

		# Determine the rotation direction
		PdotN = P[0]*N[0] + P[1]*N[1]
		if PdotN < 0:
			rot = angle # counter-clockwise
		else:
			rot = -1.0 * angle	# clockwise

		# get and set the Joint Angles 
		jointAngles = OpenMaya.MDoubleArray()
		try:
			self._getJointAngles(jointAngles)
		except:
			# getting angles failed, do nothing
			pass
		else:
			jointAngles.set(jointAngles[0] + rot, 0)
			self._setJointAngles(jointAngles)


##############################################################################


def nodeCreator():
	return OpenMayaMPx.asMPxPtr(simpleSolverNode())


def nodeInitializer():
	# nothing to initialize
	pass


# initialize the script plug-in
def initializePlugin(mobject):
	mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")

	try:
		mplugin.registerNode(kSolverNodeName, kSolverNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kIkSolverNode)
	except:
		sys.stderr.write("Failed to register node: %s" % kSolverNodeName)
		raise


# uninitialize the script plug-in
def uninitializePlugin(mobject):
	mplugin = OpenMayaMPx.MFnPlugin(mobject)
	try:
		mplugin.deregisterNode(kSolverNodeId)
	except:
		sys.stderr.write("Failed to unregister node: %s" % kSolverNodeName)
		raise
